/*
 *  Copyright 2019-2023 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.zhengjie.modules.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alipay.api.domain.Amount;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wechat.pay.java.service.refund.model.AmountReq;
import me.zhengjie.exception.BizException;
import me.zhengjie.modules.order.domain.OrderDetail;
import me.zhengjie.modules.order.domain.PayOrder;
import me.zhengjie.modules.order.domain.SoldDetail;
import me.zhengjie.modules.order.domain.SoldOrder;
import me.zhengjie.modules.order.domain.req.ApplySoldReq;
import me.zhengjie.modules.order.domain.req.OrderAuditReq;
import me.zhengjie.modules.order.domain.resp.OrderListResp;
import me.zhengjie.modules.order.domain.resp.SoldOrderDetailResp;
import me.zhengjie.modules.order.domain.resp.SoldOrderListResp;
import me.zhengjie.modules.order.domain.vo.SoldDetailQueryCriteria;
import me.zhengjie.modules.order.mapper.SoldDetailMapper;
import me.zhengjie.modules.order.service.OrderDetailService;
import me.zhengjie.modules.order.service.PayOrderService;
import me.zhengjie.modules.order.service.SoldDetailService;
import me.zhengjie.modules.pay.domain.dto.ProductDTO;
import me.zhengjie.modules.pay.domain.req.RefundReq;
import me.zhengjie.modules.pay.domain.req.WxPayReq;
import me.zhengjie.modules.pay.enums.StatusEnum;
import me.zhengjie.modules.pay.service.WxPayService;
import me.zhengjie.modules.shop.domain.Product;
import me.zhengjie.modules.shop.domain.ProductKind;
import me.zhengjie.modules.shop.mapper.ProductKindMapper;
import me.zhengjie.modules.shop.mapper.ProductMapper;
import me.zhengjie.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import me.zhengjie.modules.order.service.SoldOrderService;
import me.zhengjie.modules.order.domain.vo.SoldOrderQueryCriteria;
import me.zhengjie.modules.order.mapper.SoldOrderMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import me.zhengjie.utils.PageUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.io.IOException;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;

import me.zhengjie.utils.PageResult;

import static me.zhengjie.modules.base.utils.UnitConvertUtil.priceInCents;
import static me.zhengjie.modules.base.utils.UnitConvertUtil.priceInYuan;
import static me.zhengjie.modules.biz.constant.GlobalConstant.VIP_ORDER;
import static me.zhengjie.modules.biz.domain.enums.ErrorCode.*;
import static me.zhengjie.modules.biz.util.OrderSnCreate.createOrderSn;
import static me.zhengjie.modules.pay.enums.StatusEnum.*;

/**
 * @description 服务实现
 * @author yiwei.he
 * @date 2024-04-25
 **/
@Service
@RequiredArgsConstructor
public class SoldOrderServiceImpl extends ServiceImpl<SoldOrderMapper, SoldOrder> implements SoldOrderService {

    private final SoldOrderMapper soldOrderMapper;
    private final SoldDetailService soldDetailService;
    private final PayOrderService payOrderService;
    private final OrderDetailService orderDetailService;
    private final ProductKindMapper productKindMapper;
    private final ProductMapper productMapper;
    private final WxPayService wxPayService;

    @Override
    public PageResult<SoldOrder> queryAll(SoldOrderQueryCriteria criteria, Page<Object> page){
        return PageUtil.toPage(soldOrderMapper.findAll(criteria, page));
    }

    @Override
    public List<SoldOrder> queryAll(SoldOrderQueryCriteria criteria){
        return soldOrderMapper.findAll(criteria);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(SoldOrder resources) {
        save(resources);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(SoldOrder resources) {
        SoldOrder soldOrder = getById(resources.getId());
        soldOrder.copy(resources);
        saveOrUpdate(soldOrder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteAll(List<Long> ids) {
        removeBatchByIds(ids);
    }

    @Override
    public void download(List<SoldOrder> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (SoldOrder soldOrder : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("用户id", soldOrder.getUserId());
            map.put("订单id", soldOrder.getOId());
            map.put("售后订单号", soldOrder.getOrderSn());
            map.put("退款上传凭证", soldOrder.getImgs());
            map.put("补充描述 ", soldOrder.getReason());
            map.put("退款金额", soldOrder.getMoney());
            map.put("提交时间", soldOrder.getCreateTime());
            map.put("更新时间", soldOrder.getUpdateTime());
            map.put("1.待审核 2.已审核待收货  3.已完成 4.已拒绝", soldOrder.getStatus());
            map.put("拒绝理由", soldOrder.getRefuseReason());
            map.put("售后类型 1-退款无需退货 2-退款退货", soldOrder.getSoldType());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public PageResult<SoldOrderListResp> list(SoldOrderQueryCriteria criteria, Page<Object> page) {
        IPage<SoldOrderListResp> list = soldOrderMapper.list(criteria, page);
        for (SoldOrderListResp record : list.getRecords()) {
            record.setTotalPrice(record.getMoney());
            record.setRealPrice(record.getMoney());
        }
        return PageUtil.toPage(list);
    }

    @Override
    public SoldOrderDetailResp detail(String orderSn) {
        return soldOrderMapper.detail(orderSn);
    }

    /**
     * 保存需要售后的商品信息，将tz_pay_order表中的订单改为售后状态
     * @param req
     * @return
     * @throws BizException
     */
    @Override
    @Transactional
    public Boolean apply(ApplySoldReq req) throws BizException {
        // 查看订单是否已经是售后订单，如果是，抛出异常
        PayOrder order = payOrderService.getById(req.getOId());
        checkRequest(order);
        List<Long> ids = new ArrayList<>();
        List<Long> productIds = new ArrayList<>();
        Map<Long, ProductKind> kindMap = new HashMap<>();
        Map<Long, Product> productMap = new HashMap<>();
        // 1.检查请求和商品列表
        List<ProductDTO> productDTOList = req.getProductDTOList();
        if (productDTOList.isEmpty()) {
            throw new BizException(BODY_NOTNULL.getCode(), BODY_NOTNULL.getMessage());
        }
        productDTOList.forEach(dto -> {
            ids.add(dto.getKId());
            productIds.add(dto.getPId());
        });
        List<ProductKind> productKinds = productKindMapper.selectList(new QueryWrapper<ProductKind>().in("id", ids));
        List<Product> products = productMapper.selectList(new QueryWrapper<Product>().in("id", productIds));
        // 封装商品种类对象
        productKinds.forEach(kind -> kindMap.put(kind.getId(),kind));
        products.forEach( p -> productMap.put(p.getId(),p));
        // 2.计算退款金额
        BigDecimal refundMoney = BigDecimal.ZERO;
        for (ProductDTO dto : productDTOList) {
            refundMoney = refundMoney.add(kindMap.get(dto.getKId()).getPrice().multiply(BigDecimal.valueOf(dto.getNum())));
        }
        BigDecimal resourceMoney = priceInYuan(Long.valueOf(order.getTotal()));// 原订单总金额
        if(refundMoney.compareTo(resourceMoney) > 0){
            throw new BizException(REFUND_MONEY_ERROR.getCode(),REFUND_MONEY_ERROR.getMessage());
        }
        // 3.保存售后订单信息，编辑支付订单信息
        editOrder(req, refundMoney, productDTOList, kindMap, productMap);
        return true;
    }

    private static void checkRequest(PayOrder order) throws BizException {
        if(order == null){
            throw new BizException(ORDER_IS_NOT_EXIST.getCode(), ORDER_IS_NOT_EXIST.getMessage());
        }
        // 每笔订单只能售后一次
        if(order.getSoldStatus() != SOLD_DEFAULT.getCode()){
            throw new BizException(ORDER_ALREADY_SOLD.getCode(), ORDER_ALREADY_SOLD.getMessage());
        }
        // vip订单不能售后
        if(order.getOrderType().equals(VIP_ORDER)){
            throw new BizException(VIP_ORDER_NOT_SUPPORT.getCode(), VIP_ORDER_NOT_SUPPORT.getMessage());
        }
    }

    private void editOrder(ApplySoldReq req, BigDecimal money, List<ProductDTO> productDTOList, Map<Long, ProductKind> kindMap, Map<Long, Product> productMap) {
        SoldOrder soldOrder = new SoldOrder();
        BeanUtil.copyProperties(req, soldOrder);
        soldOrder.setOrderSn(createOrderSn());
        soldOrder.setSoldType(req.getSoldType());
        soldOrder.setCreateTime(LocalDateTime.now());
        soldOrder.setMoney(money);
        soldOrder.setStatus(AUDIT_DEFAULT.getCode());
        // 4.保存订单
        this.save(soldOrder);
        // 5.保存订单明细
        List<SoldDetail> soldDetails = productDTOList.stream()
                .map(dto -> {
                    SoldDetail soldDetail = new SoldDetail();
                    BeanUtil.copyProperties(dto, soldDetail);
                    soldDetail.setName(productMap.get(dto.getPId()).getName());
                    soldDetail.setPrice(kindMap.get(dto.getKId()).getPrice());
                    soldDetail.setCover(productMap.get(dto.getPId()).getCover());
                    soldDetail.setSId(soldOrder.getId());
                    soldDetail.setPId(dto.getPId());
                    soldDetail.setTotalPrice(kindMap.get(dto.getKId()).getPrice().multiply(BigDecimal.valueOf(dto.getNum())));
                    return soldDetail;
                })
                .collect(Collectors.toList());
        soldDetailService.saveBatch(soldDetails);
        // 6.更新支付订单和购买订单状态
        Long orderId = req.getOId();
        orderDetailService.update(new UpdateWrapper<OrderDetail>()
                .eq("o_id", orderId)
                .in("k_id", productDTOList.stream().map(ProductDTO::getKId).collect(Collectors.toList()))
                .set("sold_status", SOLD_START.getCode()));
        payOrderService.update(new UpdateWrapper<PayOrder>()
                .eq("id", orderId)
                .set("sold_status", SOLD_START.getCode()));
    }

    @Override
    @Transactional
    public Boolean audit(OrderAuditReq req) throws BizException {
        // 如果售后订单不存在，抛出异常
        SoldOrder soldOrder = getById(req.getId());
        if(soldOrder == null){
            throw new BizException(ORDER_IS_NOT_EXIST.getCode(), ORDER_IS_NOT_EXIST.getMessage());
        }
        switch (req.getStatus()) {
            case 2:
                return awaitAudit(req,soldOrder);
            case 3:
                return finish(req,soldOrder);
            case 4:
                return reject(req,soldOrder);
            default:
                return false;
        }
    }


    private Boolean awaitAudit(OrderAuditReq req,SoldOrder soldOrder) {
        // 待审核 -> 已审核
        payOrderService.update(new UpdateWrapper<PayOrder>()
                .eq("id",soldOrder.getOId())
                .set("sold_status", AUDIT_START.getCode())
        );
        return this.update(new UpdateWrapper<SoldOrder>()
                .eq("id", req.getId())
                .set("status", AUDIT_START.getCode())
                .set("update_time", LocalDateTime.now()));
    }

    private Boolean finish(OrderAuditReq req,SoldOrder soldOrder) throws BizException {
        // 已完成
        // 查询原订单总金额
        PayOrder payOrder = payOrderService.getById(soldOrder.getOId());
        payOrderService.update(new UpdateWrapper<PayOrder>()
                .eq("id",soldOrder.getId())
                .set("sold_status", AUDIT_END.getCode())
        );
        boolean res = this.update(new UpdateWrapper<SoldOrder>()
                .eq("id", req.getId())
                .set("status", AUDIT_END.getCode())
                .set("update_time", LocalDateTime.now()));
        if (res) {
            // 调用微信退款的api
            RefundReq refundReq = new RefundReq();
            AmountReq amount = new AmountReq();
            amount.setCurrency("CNY");
            amount.setTotal(Long.valueOf(payOrder.getTotal()));
            amount.setRefund((long) priceInCents(soldOrder.getMoney()));
            refundReq.setAmountReq(amount);
            refundReq.setOrderSn(payOrder.getOrderSn());
            refundReq.setOutRefundNo(soldOrder.getOrderSn());
            refundReq.setTransactionId(payOrder.getTransactionId());
            refundReq.setReason(soldOrder.getReason());
            return wxPayService.refundOrder(refundReq);
        }
        return false;
    }

    private Boolean reject(OrderAuditReq req,SoldOrder soldOrder) {
        // 拒绝：修改状态，在售后订单表中填写原因
        payOrderService.update(new UpdateWrapper<PayOrder>()
                .eq("id",soldOrder.getOId())
                .set("sold_status", AUDIT_REJECTED.getCode())
        );
        return this.update(new UpdateWrapper<SoldOrder>()
                .eq("id", req.getId())
                .set("status", AUDIT_REJECTED.getCode())
                .set(req.getRefuseReason() != null,"refuse_reason", req.getRefuseReason())
                .set("update_time", LocalDateTime.now()));
    }
}